﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Configuration;
using System.Net;
using System.Net.Mail;
using NLog;
using System.IO;
using Ionic.Zip;
using Gollie.Mail;

namespace Gollie.Administration.Service.Tasks
{
	public class BackupMySql : IAdministrationTask
	{
		private static readonly Logger logger = LogManager.GetCurrentClassLogger();

		CancellationToken ct;

		TaskProperties taskProperties;

		public void Run(CancellationToken ct, TaskProperties properties)
		{
			this.ct = ct;

			this.taskProperties = properties;

			try
			{
				this.Backup();

				this.ZipAndMail();
			}
			catch (Exception e)
			{
				logger.FatalException("Backup failed: {0}", e);
			}
		}

		private void Backup()
		{
			logger.Info("Performing sqldump...");
			string backupCode = ConfigurationManager.AppSettings["mysqlbackupcode"];

			ExecuteCommand(backupCode);
			logger.Info("Sqldump finished.");
		}

		private void ExecuteCommand(string command)
		{
			int exitCode;
			ProcessStartInfo processInfo;
			Process process;

			processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
			processInfo.CreateNoWindow = true;
			processInfo.UseShellExecute = false;
			// *** Redirect the output ***
			processInfo.RedirectStandardError = true;
			processInfo.RedirectStandardOutput = true;

			process = Process.Start(processInfo);
			process.WaitForExit();

			// *** Read the streams ***
			string output = process.StandardOutput.ReadToEnd();
			string error = process.StandardError.ReadToEnd();

			exitCode = process.ExitCode;

			logger.Info("Backup, exitcode: {0}", exitCode);

			process.Close();
		}

		private void ZipAndMail()
		{
			string backupDataFolder = ConfigurationManager.AppSettings["mysqlbackupdatafolder"];
			string zippw = ConfigurationManager.AppSettings["mysqlbackupzippassword"];

			string backupIn = GetLatestSql(backupDataFolder);
			if (backupIn == null)
				throw new Exception("No sql file found in " + backupDataFolder);

			logger.Info("Zipping {0}...", backupIn);

			string backupOut = Path.ChangeExtension(backupIn, ".zip");

			FileStream outputStream = new FileStream(backupOut, FileMode.Create, FileAccess.Write);

			using (ZipFile zip = new ZipFile())
			{
				zip.Password = zippw;
				zip.AddFile(backupIn, "");

				zip.Save(outputStream);
			}

			outputStream.Close();

			logger.Info("Zipped {0}", backupIn);

			DeleteOldDumps(backupDataFolder);

			this.MailDbBackup(backupOut);

			//no need to keep the sql if we have the zip on disk and emailed.
			File.Delete(backupIn);
		}

		private void DeleteOldDumps(string folder)
		{
			logger.Info("Deleting old dumps");

			int expirationInDays = System.Xml.XmlConvert.ToInt32(ConfigurationManager.AppSettings["mysqlbackupexpiredays"]);

			AdministrationService.DeleteOldFiles(folder, "*.zip", expirationInDays, logger);
		}

		private string GetLatestSql(string folder)
		{
			string[] files = Directory.GetFiles(folder, "*.sql", SearchOption.TopDirectoryOnly);

			string newest = null;

			DateTime crea = DateTime.MinValue;
			foreach (string file in files)
			{
				DateTime dt = File.GetCreationTime(file);
				if (dt > crea)
				{
					newest = file;
					crea = dt;
				}
			}

			return newest;
		}

		private void MailDbBackup(string path)
		{
			logger.Info("Mailing {0}...", path);

			MailClient.Queue(new EMail
			{
				AttachmentPath = path,
				Name = "mysqldump",
				Recipients = ConfigurationManager.AppSettings["backuprecipients"],
				Args = new Dictionary<string, string>()
						{
							{"{timestamp}", DateTime.Now.ToString("yyyy-MMM-dd, HH:mm:ss") }
						}
			});
		}
	}
}
